// Server 包是我的一个服务集合,包括数据库连接信息都将在这里初始化,
// 供给全局使用
package server

import (
	"github.com/jinzhu/gorm"
	"github.com/spf13/viper"
	"github.com/xhyonline/websocket/mod"
	"github.com/xhyonline/xutil/db"
	"github.com/xhyonline/xutil/xlog"
	"gopkg.in/olahol/melody.v1"
	"strconv"
	"strings"
	"sync"
)

var (
	once = new(sync.Once)
	// 实例
	instance *server
	// 日志文件
	log = xlog.Get(false, "./log.txt")
)

// server 是一个 API 与数据库的服务集合
type server struct {
	// 数据库连接对象
	DB *gorm.DB
	// socket 实例
	Socket *melody.Melody
	// 用户集合 key 为用户连接 socket 时生成的 ID , val 为用户基本信息
	Users sync.Map
	// 房间组 key 为房间编号, v 房间 map[string]*mod.RoomID
	RoomGroup sync.Map
	// 统计当前在线的总人数
	UserCount *int32
	// 女性数量
	FemaleCount *int32
	// 男性数量
	MaleCount *int32
	// 用户匹配等待队列
	WaitQueue chan *mod.User
	// 话题组
	Topics []string
	// 配置
	Config *viper.Viper
}

// GetServer 单例获取一个 Server
func GetServer() *server {
	once.Do(func() {
		// 读取配置文件
		v := viper.New()
		v.SetConfigName("config")
		v.AddConfigPath(".")
		err := v.ReadInConfig()
		v.SetConfigType("yaml")
		if err != nil {
			panic(err)
		}

		// 读取 sql 的配置文件路径
		mConf := v.GetStringMapString("mysql")
		if strings.TrimSpace(mConf["max_active_conn"]) == "" {
			mConf["max_active_conn"] = "100"
		}
		if strings.TrimSpace(mConf["max_idle_conn"]) == "" {
			mConf["max_idle_conn"] = "10"
		}
		dbConf := &db.Config{
			Host:     mConf["host"],
			Port:     mConf["port"],
			User:     mConf["user"],
			Password: mConf["password"],
			Name:     mConf["name"],
			Lifetime: 3600,
		}
		active, err := strconv.Atoi(mConf["max_active_conn"])
		if err != nil {
			panic("配置文件中最大连接数必须为整数")
		}

		idle, err := strconv.Atoi(mConf["max_idle_conn"])

		if err != nil {
			panic("配置文件中最大空闲连接数必须为整数")
		}
		dbConf.MaxActiveConn = active
		dbConf.MaxIdleConn = idle
		instance = &server{
			DB:          db.NewDataBase(dbConf),
			UserCount:   new(int32),
			FemaleCount: new(int32),
			MaleCount:   new(int32),
			WaitQueue:   make(chan *mod.User, 1000),
			Topics:      v.GetStringSlice("topics"),
			Config:      v,
		}
		// 开启处理队列事件
		go instance.HandleQueue()
		// 异步定时清除无人的房间
		go instance.RemoveRoom()
	})
	return instance
}
